package com.openspring.framework.dao;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import com.openspring.framework.annotation.persistence.GeneratedValue;
import com.openspring.framework.annotation.persistence.Column;
import com.openspring.framework.annotation.persistence.Id;
import com.openspring.framework.annotation.persistence.IdClass;
import com.openspring.framework.annotation.persistence.Join;
import com.openspring.framework.annotation.persistence.Table;
import com.openspring.framework.annotation.persistence.Transient;
import com.openspring.framework.extend.spring.EntityPropertyRowMapper;
import com.openspring.framework.jdbc.sql.Delete;
import com.openspring.framework.jdbc.sql.Insert;
import com.openspring.framework.jdbc.sql.Select;
import com.openspring.framework.jdbc.sql.Update;
import com.openspring.framework.util.StringUtils;

/**
 * 泛型DAO的Spring JDBC实现抽象类
 * @author HeBin
 *
 * @param <T> 实体
 * @param <ID> 主键(联合主键使用注解@Id@IdClass)
 */
public abstract class AbstractSpringGenericDAO<T, ID extends Serializable> extends AbstractGenericDAO<T, ID> {

	private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(AbstractSpringGenericDAO.class);
	private String schema = "public";
	private String tableName;
	private String primaryKeyColumn;
	private String primaryKeyProperty;
	
	/**数据库主键是否自增*/
	private boolean auto = false;
	/**是否联合主键*/
	private boolean isCompositeId = false;
	
	private Insert insert = new Insert();
	private Update update = new Update();
	private Delete delete = new Delete();
	private Select select = new Select();
	
	
	private JdbcTemplate jdbcTemplate;
	private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
	
	public AbstractSpringGenericDAO() {}
	
	@Resource(name="dataSource")
	public void setDataSource(DataSource dataSource) {
		jdbcTemplate = new JdbcTemplate(dataSource);
		namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
		return namedParameterJdbcTemplate;
	}

	@Override
	public int insert(T entity) {
		String sql = insert.toInsert();
		SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(entity);
		if(auto) {//自增主键赋值
			KeyHolder keyHolder = new GeneratedKeyHolder();
			int count = namedParameterJdbcTemplate.update(sql, namedParameters, keyHolder, new String[]{primaryKeyColumn});
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(entity);
			bw.setPropertyValue(primaryKeyProperty, keyHolder.getKey());
			return count;
		}
		return namedParameterJdbcTemplate.update(sql, namedParameters);
	}

	@Override
	public int[] batchInsert(T[] entities) {
		String sql = insert.toInsert();
		SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(entities);
        return namedParameterJdbcTemplate.batchUpdate(sql, batch);
	}

	@Override
	public int delete(ID id) {
		String sql = delete.toDeleteById();
		if(isCompositeId) {
			SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(id);
			return namedParameterJdbcTemplate.update(sql, namedParameters);
		}
		return jdbcTemplate.update(sql, id);
	}

	@Override
	public int[] batchDelete(ID[] ids) {
		String sql = delete.toDeleteById();
		if(isCompositeId) {
			List<SqlParameterSource> batch = new ArrayList<SqlParameterSource>();
	        for(ID id : ids) {
	        	SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(id);
	            batch.add(namedParameters);
	        }
			return namedParameterJdbcTemplate.batchUpdate(sql, batch.toArray(new SqlParameterSource[]{}));
		} else {
			List<Object[]> batch = new ArrayList<Object[]>();
	        for(ID id : ids) {
	            Object[] values = new Object[] {id};
	            batch.add(values);
	        }
	        return jdbcTemplate.batchUpdate(sql, batch);
		}
	}
	
	@Override
	public int update(T entity) {
		String sql = update.toUpdateById();
		SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(entity);
		return namedParameterJdbcTemplate.update(sql, namedParameters);
	}

	@Override
	public int[] batchUpdate(T[] entities) {
		String sql = update.toUpdateById();
		SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(entities);
        return namedParameterJdbcTemplate.batchUpdate(sql, batch);
	}

	@Override
	public T load(ID id) {
		String sql = select.toSelectById();
		List<T> entities = null;
		if(isCompositeId) {
			SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(id);
			entities = namedParameterJdbcTemplate.query(sql, namedParameters, new EntityPropertyRowMapper<T>(getPersistentClass()));
		} else
			entities = jdbcTemplate.query(sql, new EntityPropertyRowMapper<T>(getPersistentClass()), id);
		return entities.isEmpty() ? null : entities.get(0);
	}
	
	@Override
	public List<T> queryAll() {
		String sql = select.toSelect();
		return jdbcTemplate.query(sql, new EntityPropertyRowMapper<T>(getPersistentClass()));
	}	
	
	@Override
	public List<T> query(String sql, Object... values) {
		return jdbcTemplate.query(sql, new EntityPropertyRowMapper<T>(getPersistentClass()), values);
	}

	@Override
	public List<T> queryWhere(String whereSql, Object... values) {
		String sql = select.toSelect() + StringUtils.parseNull(whereSql);
		return jdbcTemplate.query(sql, new EntityPropertyRowMapper<T>(getPersistentClass()), values);
	}

	@Override
	public int getTotalCount() {
		return jdbcTemplate.queryForInt(select.toSelectCount());
	}
	
	@Override
	public int getTotalCount(String whereSql, Object... values) {
		String sql = select.toSelectCount() + StringUtils.parseNull(whereSql);
		return jdbcTemplate.queryForInt(sql, values);
	}

	@Override
	public boolean isExist(ID id) {
		String sql = select.toSelectCountById();
		if(isCompositeId) {
			SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(id);
			return namedParameterJdbcTemplate.queryForInt(sql, namedParameters) > 0;
		}
		return jdbcTemplate.queryForInt(sql, id) > 0;
	}

	@Override
	public boolean isExist(String whereSql, Object... values) {
		String sql = select.toSelectCount() + StringUtils.parseNull(whereSql);
		return jdbcTemplate.queryForInt(sql, values) > 0;
	}
	
	public String getTableNameWithSchema() {
		return this.schema + "." + this.tableName;
	}
	
	@PostConstruct
	public void buildSQL() {
		String alias = "t0";
		tableName = StringUtils.addQuotesHasUpperCase(getPersistentClass().getSimpleName());//默认数据库表名为实体类名
		//判断实体类上有无Table注解以确定数据库表名
		Table table = getPersistentClass().getAnnotation(Table.class);
		if(table != null) {
			if(!StringUtils.isEmpty(table.name())) {
				tableName = StringUtils.addQuotesHasUpperCase(table.name());
			}
			if(!StringUtils.isEmpty(table.schema())) {
				schema = StringUtils.addQuotesHasUpperCase(table.schema());
			}
			if(!StringUtils.isEmpty(table.alias())) {
				alias = StringUtils.addQuotesHasUpperCase(table.alias());
			}
		}
		String tableNameWithSchema = getTableNameWithSchema();
		if(logger.isDebugEnabled())
	    	logger.debug("Table: " + tableNameWithSchema);
		insert.setTableName(tableNameWithSchema);
		update.setTableName(tableNameWithSchema);
		delete.setTableName(tableNameWithSchema);
		select.setTableName(tableNameWithSchema.concat(" ").concat(alias));
		Join join = getPersistentClass().getAnnotation(Join.class);
		if(join != null) {
			select.setTableName(tableNameWithSchema.concat(" ").concat(alias).concat(" ").concat(join.value()));
		}
		IdClass idCls = getPersistentClass().getAnnotation(IdClass.class);
		if(idCls != null) {
			isCompositeId = true;
		}
		//遍历实体类上的方法,通过一些注解确定主键和其他字段
		PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(getPersistentClass());
		for(PropertyDescriptor pd : pds) {
			//实体字段
			String propertyName = pd.getName();
			//数据库字段，默认与实体字段相同
			String columnName = StringUtils.addQuotesHasUpperCase(propertyName);
			//排除getClass()
			if(columnName.equalsIgnoreCase("class"))
				continue;
			Method method = pd.getReadMethod();
			if(method.getAnnotation(Transient.class) != null)
				continue;
			Column column = method.getAnnotation(Column.class);
			Id id = method.getAnnotation(Id.class);
			GeneratedValue gv = method.getAnnotation(GeneratedValue.class);
			//判断get上有无Column注解以确定数据库字段
			if(column != null) {
				if(!StringUtils.isEmpty(column.name())) {
					columnName = StringUtils.addQuotesHasUpperCase(column.name());
				}
				if(!StringUtils.isEmpty(column.table())) {
					alias = StringUtils.addQuotesHasUpperCase(column.table());
				}
			}
			//注解Id确定主键,AutoIncrement确定是否自增长
			if(id != null) {
				primaryKeyProperty = pd.getName();
				primaryKeyColumn = columnName;
				update.addPrimaryKeyColumn(columnName, ":" + propertyName);
				if(gv != null)
					auto = true;
				else
					insert.addColumn(columnName, ":" + propertyName);
				if(isCompositeId) {
					select.addPrimaryKeyColumn(alias + '.' + columnName, ":" + propertyName);
					delete.addPrimaryKeyColumn(columnName, ":" + propertyName);
				} else {
					select.addPrimaryKeyColumn(alias + '.' + columnName, "?");
					delete.addPrimaryKeyColumn(columnName, "?");
				}
				if(logger.isDebugEnabled())
			    	logger.debug("Primary Key Column: " + columnName + ", Primary Key Property: " + propertyName + ", AutoIncrement: " + auto);
			} else {
				if(logger.isDebugEnabled())
					logger.debug("Column: " + columnName + ", Property: " + propertyName);
				//insertable为true的添加到新增字段名集合
				if(column == null || column.insertable())
					insert.addColumn(columnName, ":" + propertyName);
				//updateatable为true的添加到修改字段名集合
				if(column == null || column.updatable())
					update.addColumn(columnName, ":" + propertyName);
			}
			select.addColumn(alias + '.' + columnName);
		}
		System.out.println(select.toSelect());
		System.out.println(select.toSelectById());
		System.out.println(select.toSelectCount());
		System.out.println(select.toSelectCountById());
		System.out.println(insert.toInsert());
		System.out.println(delete.toDeleteById());
		System.out.println(update.toUpdateById());
	}
}
